// Copyright 2020 Proyectos y Sistemas de Mantenimiento SL (eProsima).
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------
// Modification history:
// feature: support config log level by cmd tool
// feature: add statictic lookup
// ------------------------------------------------------------------

#ifndef INCLUDE_EDDS_DDS_BUILTIN_LOOKUP_LOOKUPMANAGER_HPP_
#define INCLUDE_EDDS_DDS_BUILTIN_LOOKUP_LOOKUPMANAGER_HPP_
#ifndef DOXYGEN_SHOULD_SKIP_THIS_PUBLIC

#include <edds/rtps/builtin/data/WriterProxyData.h>
#include <edds/rtps/builtin/data/ReaderProxyData.h>
#include <edds/rtps/builtin/BuiltinProtocols.h>
#include <edds/dds/builtin/lookup/LookupRequestListener.hpp>
#include <edds/dds/builtin/lookup/LookupReplyListener.hpp>
#include <edds/dds/builtin/lookup/common/LookupTypes.hpp>
#include <deps/resources/TimedEvent.h>

#include <future>
#include <vector>
#include <mutex>

namespace vbs {
namespace common {
class ReaderHistory;
class WriterHistory;
}  // namespace common
}  // namespace vbs
namespace evbs {
namespace ertps {

namespace rtps {

class BuiltinProtocols;
class RTPSParticipantImpl;
class StatefulReader;
class StatefulWriter;
class ParticipantProxyData;

}  // namespace rtps
}  // namespace ertps

namespace edds {
namespace dds {
namespace builtin {

extern const ertps::rtps::SampleIdentity INVALID_SAMPLE_IDENTITY;

struct MultiPktWrtierReaderInfos {
    std::promise<std::vector<StatisticWrtierReaderInfos>> wr_promisers_;
    std::vector<StatisticWrtierReaderInfos> infos;
};
/**
 * Class LookupManager that implements the Lookup Service described in the DDS-XTYPES 1.2
 * specification.
 * @ingroup XTYPES
 */
class LookupManager {
    friend class LookupRequestListener;
    friend class LookupReplyListener;

 public:
    /**
     * Constructor
     * @param prot Pointer to the BuiltinProtocols object.
     */
    LookupManager(ertps::rtps::BuiltinProtocols* prot);

    virtual ~LookupManager();

    /**
     * Initialize the LookupManager protocol.
     * @param p Pointer to the RTPS participant implementation.
     * @return true if the initialziacion was successful.
     */
    bool init_lookup_service(ertps::rtps::RTPSParticipantImpl* p);

    /**
     * Assign the remote endpoints for a newly discovered RTPSParticipant.
     * @param pdata Pointer to the RTPSParticipantProxyData object.
     * @return True if correct.
     */
    bool assign_remote_endpoints(const ertps::rtps::ParticipantProxyData& pdata);

    /**
     * Remove remote endpoints from the lookup service.
     * @param pdata Pointer to the ParticipantProxyData to remove
     */
    void remove_remote_endpoints(ertps::rtps::ParticipantProxyData* pdata);

    /**
     * Get the builtin protocols
     * @return Builtin protocols
     */
    ertps::rtps::BuiltinProtocols* get_builtin_protocols() { return builtin_protocols_; }

    ertps::rtps::DiscoveryProtocol get_discovery_protocol() {
        return builtin_protocols_->m_att.discovery_config.discoveryProtocol;
    }

    bool use_client() { return builtin_protocols_->m_att.lookup_config.use_client; }

    bool use_server() { return builtin_protocols_->m_att.lookup_config.use_server; }

    void type_info_reply(const std::string& type_name, const ertps::rtps::SampleIdentity& requestId);

    LookupRequestListener* get_lookupReqListener() { return server_listener_; }

    /* TODO Uncomment if security is implemented.
     #if HAVE_SECURITY
        bool pairing_remote_reader_with_local_writer_after_security(
                const ertps::rtps::GUID_t& local_writer,
                const ertps::rtps::ReaderProxyData& remote_reader_data);

        bool pairing_remote_writer_with_local_reader_after_security(
                const ertps::rtps::GUID_t& local_reader,
                const ertps::rtps::WriterProxyData& remote_writer_data);
     #endif
     */

    ertps::rtps::SampleIdentity get_type_dependencies(const ertps::rtps::GUID_t& dst_guid,
                                                      const ertps::types::TypeIdentifierSeq& in);

    ertps::rtps::SampleIdentity get_types(const ertps::rtps::GUID_t& dst_guid,
                                          const ertps::types::TypeIdentifierSeq& in);

    ertps::rtps::SampleIdentity get_type_info(const std::string& type_name);

    bool register_remote_type(const std::string& type_name);

    bool type_has_registered(const std::string& type_name);

    StatisticWrtierReaderInfos lookup_writer_reader_infos(const ertps::rtps::GUID_t& guid, uint32_t timeout);

    StatisticMatchGuids lookup_remote_guids(const ertps::rtps::GUID_t& guid, uint32_t timeout);

    StatisticProxyInfos lookup_writer_reader_proxy_infos(const ertps::rtps::GUID_t& guid, uint32_t timeout);

    evbs::ReturnCode_t lookup_statistic_send_info(const ertps::rtps::GUID_t& guid, StatisticSendInfo* info,
                                                  uint32_t timeout);

    evbs::ReturnCode_t lookup_statistic_recv_info(const ertps::rtps::GUID_t& guid, StatisticRecvInfo* info,
                                                  uint32_t timeout);

    evbs::ReturnCode_t lookup_statistic_writer_reader_qos(const ertps::rtps::GUID_t& guid,
                                                          StatisticWriterReaderQos* info, uint32_t timeout);

    Log::GuidMessageBriefsMap lookup_statistic_message_brief(const std::string& host_name, uint32_t pid,
                                                             const std::string& topic_name, uint32_t timeout);

    std::string lookup_statistic_dserver_info(const std::string& host_name, const std::string& params,
                                              uint32_t timeout);

    ReturnCode_t remoteConfig(const std::string& host_name, uint32_t pid, const std::string& params, uint32_t timeout);

    static ReturnCode_t localConfig(const std::string& params);

    void restart_timer();

    void add_unreply_sample_id(const std::string& type_name, const ertps::rtps::SampleIdentity& sample_id);

    void remove_unreply_sample_id(const std::string& type_name);

 private:
    bool request_sync_to_server();

    bool type_request_period();

    bool add_change_with_check_full(vbs::common::WriterHistory* history, vbs::common::CacheChange_t* change);

    /**
     * Create the endpoints used in the LookupManager.
     * @return true if correct.
     */
    bool create_endpoints();

    /**
     * Get the RTPS participant
     * @return RTPS participant
     */
    inline ertps::rtps::RTPSParticipantImpl* get_RTPS_participant() { return participant_; }

    //! Get out instanceName as defined in 7.6.2.3.4 of the XTypes 1.2 document
    std::string get_instanceName() const;

    //! Aux method to send requests
    bool send_request(Lookup_Request& req);

    //! Aux method to send replies
    bool send_reply(Lookup_Reply& rep);

    //! Aux method to received requests
    bool recv_request(vbs::common::CacheChange_t& change, Lookup_Request& req) const;

    //! Aux method to received replies
    bool recv_reply(vbs::common::CacheChange_t& change, Lookup_Reply& rep, uint16_t& continue_recvpkt_flag) const;

    const ertps::rtps::GUID_t& get_builtin_request_writer_guid() const;

    const ertps::rtps::GUID_t& get_builtin_reply_writer_guid() const;

    vbs::common::TimedEvent* lookup_timer_ = nullptr;

    //! Pointer to the local RTPSParticipant.
    ertps::rtps::RTPSParticipantImpl* participant_;

    //! Pointer to the builtinprotocol class.
    ertps::rtps::BuiltinProtocols* builtin_protocols_;

    //! Pointer to the builtinRTPSParticipantMEssageWriter.
    ertps::rtps::StatefulWriter* builtin_request_writer_;

    //! Pointer to the builtinRTPSParticipantMEssageReader.
    ertps::rtps::StatefulReader* builtin_request_reader_;

    //! Pointer to the builtinRTPSParticipantMEssageWriter.
    ertps::rtps::StatefulWriter* builtin_reply_writer_;

    //! Pointer to the builtinRTPSParticipantMEssageReader.
    ertps::rtps::StatefulReader* builtin_reply_reader_;

    //! Writer History
    vbs::common::WriterHistory* builtin_request_writer_history_;

    //! Writer History
    vbs::common::WriterHistory* builtin_reply_writer_history_;

    //! Reader History
    vbs::common::ReaderHistory* builtin_request_reader_history_;

    //! Reader History
    vbs::common::ReaderHistory* builtin_reply_reader_history_;

    //! Request Listener object.
    LookupRequestListener* server_listener_;

    //! Reply Listener object.
    LookupReplyListener* client_listener_;

    std::mutex types_lock_;
    std::list<std::string> types_to_register_;

    std::list<std::string> types_to_sync_;

    std::mutex temp_data_lock_;
    ertps::rtps::ReaderProxyData temp_reader_proxy_data_;
    ertps::rtps::WriterProxyData temp_writer_proxy_data_;

    mutable ertps::rtps::SequenceNumber_t request_seq_number_;

    mutable Lookup_TypeSupport<Lookup_Request> request_type_;

    mutable Lookup_TypeSupport<Lookup_Reply> reply_type_;

    // key:type name, value: request id
    std::map<std::string, std::list<ertps::rtps::SampleIdentity>> unReplySampleIds_;

    std::mutex promise_lock_;
    std::map<ertps::rtps::SampleIdentity, MultiPktWrtierReaderInfos> wr_infos_;
    std::map<ertps::rtps::SampleIdentity, std::promise<StatisticMatchGuids>> guid_promises_;
    std::map<ertps::rtps::SampleIdentity, std::promise<StatisticSendInfo>> send_promises_;
    std::map<ertps::rtps::SampleIdentity, std::promise<StatisticRecvInfo>> recv_promises_;
    std::map<ertps::rtps::SampleIdentity, std::promise<StatisticWriterReaderQos>> writer_qos_promises_;
    std::map<ertps::rtps::SampleIdentity, std::promise<Log::GuidMessageBriefsMap>> msg_brief_promises_;
    std::map<ertps::rtps::SampleIdentity, std::promise<StatisticProxyInfos>> proxy_info_promises_;
    std::map<ertps::rtps::SampleIdentity, std::promise<ReturnCode_t>> remote_config_promises_;
    std::map<ertps::rtps::SampleIdentity, std::promise<std::string>> dserver_info_promises_;

    /* TODO Uncomment if security is implemented.
     #if HAVE_SECURITY
        //!Pointer to the builtinRTPSParticipantMEssageWriter.
        ertps::rtps::StatefulWriter* builtin_request_writer_secure_;

        //!Pointer to the builtinRTPSParticipantMEssageWriter.
        ertps::rtps::StatefulWriter* builtin_reply_writer_secure_;

        //!Pointer to the builtinRTPSParticipantMEssageReader.
        ertps::rtps::StatefulReader* builtin_request_reader_secure_;

        //!Pointer to the builtinRTPSParticipantMEssageReader.
        ertps::rtps::StatefulReader* builtin_reply_reader_secure_;

        //!Writer History
        vbs::common::WriterHistory* builtin_request_writer_secure_history_;

        //!Writer History
        vbs::common::WriterHistory* builtin_reply_writer_secure_history_;

        //!Reader History
        vbs::common::ReaderHistory* builtin_request_reader_secure_history_;

        //!Reader History
        vbs::common::ReaderHistory* builtin_reply_reader_secure_history_;

        bool create_secure_endpoints();
     #endif
     */
};

} /* namespace builtin */
} /* namespace dds */
} /* namespace edds */
} /* namespace evbs */
#endif  // ifndef DOXYGEN_SHOULD_SKIP_THIS_PUBLIC
#endif  // INCLUDE_EDDS_DDS_BUILTIN_LOOKUP_LOOKUPMANAGER_HPP_
